Transcrição
Nosso filtro está funcionando como gostaríamos, porém há um detalhe a respeito da última mensagem que implementamos, "Sorry, no photos", que apenas alguém mais atento conseguirá notar. Ao recarregarmos a página, a mensagem é exibida durante alguns poucos segundos, antes da listagem ser carregada. Isso é meio estranho, não?
Não fizemos uma requisição à API para que os dados das imagens fossem trazidos? Sendo assim, esta mensagem deveria aparecer somente em decorrência do filtro. Isso pode acabar deixando o usuário frustrado, ainda mais se for uma operação que acabar demorando.
Em photo-list.component.ts, a lista de imagens começa com um array vazia (photos: Photo[] = []), então, quando o componente é carregado, a lista será considerada no *ngIf e a mensagem será exibida. Depois, na inicialização ngOnInit(), os dados serão trazidos da API e colocados na propriedade photos, que então terá dados, fazendo com que a mensagem deixe de ser exibida.
O problema é que a busca destes dados está sendo feita no componente, então, para que a mensagem não apareça, o ideal é que o componente receba a lista de imagens pronta antes de navegarmos a ele. Em suma, entraremos na rota e, antes do componente ser criado e renderizado, resolveremos e disponibilizaremos os dados de que ele precisa. Deste modo, o componente receberá os dados prontos, sem precisar buscá-los, e o array de imagens será preenchido.
Podemos resolver este tipo de problema com o Angular, por meio do Resolver, capaz de lidar com dados durante a navegação de uma rota para disponibilizá-los a um componente antes deste ser carregado.
Por estar muito atrelado ao "photo-list", criaremos nosso arquivo nesta pasta, e o chamaremos de photo-list.resolver.ts. Precisamos do serviço PhotoService no construtor desta classe para resolvermos esta dependência. E sabemos que, para ser injetável, termos acesso ao PhotoService e podermos utilizar o Resolver, teremos um @Injectable().
Para ser um Resolver, a classe precisará implementar a interface Resolve, genérica, e que devolverá o tipo de dado retornado pelo listFromUser(), um Observable<Photo[]>. Ambos precisarão ser importados. Para conseguirmos trabalhar com o PhotoListResolver, implementaremos o método resolve(), e o Visual Studio está se recusando a adicioná-lo automaticamente, mas não tem problema, faremos isto manualmente.
Como parâmetro, este método receberá ActivatedRouteSnapshot, que tirará um screenshot do que estiver acontecendo no momento, e um state, RouterStateSnapshot. Teremos uma variável const userName, cujo resultado será route.params.userName. Depois, usaremos o return com this.service.listFromUser().
import { Injectable } from '@angular/core';
import { Resolve } from '@angular/router';
import { Observable } from 'rxjs';
import { PhotoService } from '../photo/photo.service';
import { Photo } from '../photo/photo';
@Injectable({ providedIn: 'root'})
export class PhotoListResolver implements Resolve<Observable<Photo[]>>{
constructor(private service: PhotoService) {}
resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
const userName = route.params.userName;
return this.service.listFromUser(userName);
}
}Assim, se passarmos o mouse sobre resolve(), será indicado que o retorno é do tipo Observable<Photo[]>, o que também pode ser explicitado usando-se dois pontos:
resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<Photo[]> {
const userName = route.params.userName;
return this.service.listFromUser(userName);
}Entretanto, ainda precisaremos acessar o arquivo de rotas, app.routing.module.ts. Como o Resolver é acionado no momento em que a rota está sendo resolvida, é ali que se encontra sua configuração. Portanto, incluiremos resolve passando um objeto JavaScript para disponibilizar a propriedade photos com o resultado de PhotoListResolver:
path: 'user/:userName',
component: PhotoListComponent,
resolve: {
photos: PhotoListResolver
}E em photo-list.component.ts, deletaremos o seguinte trecho, que passa a ser desnecessário:
const userName = this.activatedRoute
.snapshot
.params
.userName;
this.photoService
.listFromUser(userName)
.subscribe(photos => this.photos = photos);Em consequência, removeremos também private photoService: PhotoService em constructor(), e o seu respectivo importe. Em ngOnInit(), incluiremos this.photos = this.activatedRoute.snapshot.data.photos, cujo snapshot.data nos permite acessar o valor contido em photos de app.routing.module.ts.
Também é possível usar
this.activatedRoute.snapshot.data['photos'].
Com isso indicamos que é preciso buscar dados em photos, nome da propriedade usada no Routes, para esta rota que foi ativada. Salvaremos tudo, voltaremos ao navegador e acessaremos localhost:4200/user/flavio. As imagens são buscadas, e então recarregaremos a página e, em nenhum momento, a mensagem "Sorry, no photos" é exibida, pois os dados são resolvidos no sistema de rotas.
Esta é a motivação por trás do Resolver — a resolução de dados assíncronos dos quais o componente depende antes de ser ativado, no momento em que ativamos a rota, antes mesmo dela avaliar tal componente.